package cn.mesmile.admin.common.oss;

import cn.mesmile.admin.common.oss.rule.OssRuleImpl;
import cn.mesmile.admin.common.oss.template.MinioTemplate;
import io.minio.MinioClient;
import okhttp3.ConnectionPool;
import okhttp3.Dispatcher;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import javax.net.ssl.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

/**
 * @author zb
 * @Description oss相关配置
 */
@EnableConfigurationProperties({OssProperties.class})
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = {"oss.enabled"}, havingValue = "true")
public class OssConfig {

    @Resource
    private OssProperties ossProperties;

    /********************************minio相关配置*********************************/
    @Bean
    @ConditionalOnMissingBean({MinioClient.class})
    @ConditionalOnProperty(value = {"oss.type"}, havingValue = "minio_oss")
    public MinioClient minioClient() {
        Dispatcher dispatcher = new Dispatcher();
        // 设置最大请求数量
        dispatcher.setMaxRequests(128);
        // 设置每台主机的最大请求数量
        dispatcher.setMaxRequestsPerHost(10);
        OkHttpClient httpClient = new OkHttpClient()
                .newBuilder()
                .connectTimeout(5, TimeUnit.MINUTES)
                .writeTimeout(5, TimeUnit.MINUTES)
                .readTimeout(5, TimeUnit.MINUTES)
                // 支持协议
                .protocols(Collections.singletonList(Protocol.HTTP_1_1))
                // 最大空闲连接，保持活动时间
                .connectionPool(new ConnectionPool(10, 5 ,TimeUnit.MINUTES))
                .dispatcher(dispatcher)
                .build();
        String filename = System.getenv("SSL_CERT_FILE");
        if (filename != null && !filename.isEmpty()) {
            try {
                httpClient = enableExternalCertificates(httpClient, filename);
            } catch (GeneralSecurityException | IOException e) {
                throw new RuntimeException(e);
            }
        }

        return MinioClient.builder().endpoint(ossProperties.getEndpoint())
                .credentials(ossProperties.getAccessKey(), ossProperties.getSecretKey())
                .httpClient(httpClient)
                .build();
    }

    /**
     * copied logic from
     * https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/CustomTrust.java
     */
    private OkHttpClient enableExternalCertificates(OkHttpClient httpClient, String filename)
            throws GeneralSecurityException, IOException {
        Collection<? extends Certificate> certificates = null;
        try (FileInputStream fis = new FileInputStream(filename)) {
            certificates = CertificateFactory.getInstance("X.509").generateCertificates(fis);
        }

        if (certificates == null || certificates.isEmpty()) {
            throw new IllegalArgumentException("expected non-empty set of trusted certificates");
        }
        // Any password will work.
        char[] password = "password".toCharArray();

        // Put the certificates a key store.
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        // By convention, 'null' creates an empty key store.
        keyStore.load(null, password);

        int index = 0;
        for (Certificate certificate : certificates) {
            String certificateAlias = Integer.toString(index++);
            keyStore.setCertificateEntry(certificateAlias, certificate);
        }

        // Use it to build an X509 trust manager.
        KeyManagerFactory keyManagerFactory =
                KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, password);
        TrustManagerFactory trustManagerFactory =
                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(keyStore);

        final KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
        final TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(keyManagers, trustManagers, null);
        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

        return httpClient
                .newBuilder()
                .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManagers[0])
                .build();
    }

    @Bean
    @ConditionalOnBean({MinioClient.class})
    @ConditionalOnMissingBean({MinioTemplate.class})
    public MinioTemplate minioTemplate(MinioClient minioClient) {
        return new MinioTemplate(new OssRuleImpl(), minioClient, ossProperties);
    }

    /********************************aliOss相关配置*********************************/
//    @Bean
//    @ConditionalOnMissingBean({AliOssClient.class})
//    @ConditionalOnProperty(value = {"oss.type"}, havingValue = "ali_oss")
//    public AliOssClient minioClient() {
//        return AliOssClient.builder().endpoint(ossProperties.getEndpoint())
//                .credentials(ossProperties.getAccessKey(), ossProperties.getSecretKey()).build();
//    }
//
//    @Bean
//    @ConditionalOnBean({AliOssClient.class})
//    @ConditionalOnMissingBean({AliOssTemplate.class})
//    public AliOssTemplate minioTemplate(AliOssClient aliOssClient) {
//        return new AliOssTemplate(new OssRuleImpl(), aliOssClient, ossProperties);
//    }

}
